#ifndef WORLD_H
#define WORLD_H

class Regionmap;
class Loader;

#include "MainHeader.h"

#include "Item.h"
#include "IThread.h"
#include "Updatequeue.h"

#ifdef SERVER
class ServerMainThread;
#include "ServerMainThread.h"
#endif

#ifdef CLIENT
class ClientMainThread;
#include "ClientMainThread.h"
#endif

#ifdef RENDER
#include "DXUTCamera.h"
#include "Skybox.h"
#include "HUD.h"
#endif

#include "EntityItem.h"
#include "EntityAI.h"
#include "Player.h"

#include "Sound\SoundEngine.h"

class EntityWorld : public IWorld 
{
protected:
	/* Entity */
	vector<EntityItem*>		m_pEntityItems;
	vector<EntityLiving*>	m_pEntityLivings;
	vector<Player*>			m_pPlayers;

public:
	~EntityWorld();

	vector<EntityItem*> * getAllItemsInRange(D3DXVECTOR3 position, float distance);

	//Idle
	void FrameMove( double fTime, float fElapsedTime);
};

class World : public EntityWorld
{
protected:
	Regionmap *				map;
	vector<SetCallBuffer>	setcalls;

	/* Savefile */
	FILE*					m_file;
	int						m_Seed;

	//IThread*				m_pLoader;
	//IThread*				m_pUpdater;
	IThread*				m_pTickUpdater;

	bool					Dirty;

	double					m_Daytime;
	float					m_Daylength;
public:
	World();
	~World();

	inline Region * getRegion(int x, int z);
	inline Region * createRegion(int x, int z);
	inline Region * removeRegion(int x, int z);

	Block   getBlock(int x, int z, int y);
	Block * getpBlock(int x, int z, int y);
	virtual Block	breakBlock(int x, int z, int y);
	virtual int		placeBlock(int x, int z, int y, Block block);
	void setBlock(int x, int z, int y, Block block, byte flags = UPDATE_ALL);
	void update();

	float getTemperature(int x, int z);
	float getHumidity(int x, int z);
	BYTE  getBiome(int x, int z);

	byte updateBlock(int x, int z, int y, byte type, byte * ticks);
	void delayedUpdateBlock(int x, int z, int y, byte type, byte ticks);

	virtual int loadRegion(int x, int z);
	Region * unloadRegion(int x, int z, Chunk ** chunks);

	void generateStructures(int x, int z);
	void lightPass();
	void lightPass(int x, int z);
	void resetLight();
	void resetLight(int x, int z);

	//Idle
	void FrameMove( double fTime, float fElapsedTime);

	virtual void setDirty(int x, int z, int y, bool immediatly){};
	virtual int renewDirty(bool immediatly){return -1;};
	virtual int generateVB(int x, int z){return -1;};

	virtual D3DXVECTOR3 getPlayerPosition(int index);
};

#ifdef CLIENT
class RenderableWorld : public World {
protected:
	ID3D11Device*			m_pd3dDevice;
	ID3D11DeviceContext*	m_pDeferredContext;

	// Targetbox-buffer
	ID3D11Buffer*			m_pVertexBuffer;
	ID3D11Buffer*			m_pIndexBuffer;
	WORD					indexSize;

	/* Constant Buffer */
	ID3D11Buffer*			m_pcbPerFrame11;

	ID3D11Buffer*			m_pcbVSPerFrame11;
	ID3D11Buffer*			m_pcbPSPerFrame11;

	ID3D11Buffer*			m_pcbVSPerObject11;
	/* Constant Buffer */

	/* States */
	ID3D11SamplerState*		m_pSamplerStateBlock11;
	ID3D11BlendState*		m_pBlendState11;
	/* States */

	// Shader
	ID3D11VertexShader*     m_pBlockVertexShader;
	ID3D11PixelShader*      m_pBlockPixelShader;

	ID3D11VertexShader*     m_pEntityVertexShader;

	ID3D11VertexShader*     m_pBlockAlphaVertexShader;
	ID3D11PixelShader*      m_pBlockAlphaPixelShader;

	ID3D11VertexShader*     m_pVertexShader;
	ID3D11PixelShader*      m_pPixelShader;
	ID3D11InputLayout*      m_pVertexLayout;

	// Effect
	ID3D11InputLayout*          m_pLayout11;
	ID3D11ShaderResourceView*   m_pTextureRV;
	ID3D11ShaderResourceView*   m_pTextureAlphaRV;

	// Camera
	D3DXVECTOR3				m_EyePt;
	D3DXVECTOR3				m_Direction;
	CFirstPersonCamera *	m_Camera;
	bool					m_freefly;

	// Day - Night - Cycle
	D3DXVECTOR3				m_Skylight;
	D3DXVECTOR3				m_Skycolor;
	double					m_Daytime;
	Skybox*					m_Sky;
	float					m_Daylength;

	blocktype				m_bid; /* Blocktype Camera is in */
	float					m_fogStart;
	float					m_fogEnd;
	D3DXVECTOR3				m_fogColor;

	//Mouse
	bool					m_bLeftButtonDown; 
	bool					m_bRightButtonDown; 
	bool					m_bMiddleButtonDown; 
	bool					m_bSideButton1Down;
	bool					m_bSideButton2Down;
	// Keyboard
	bool					m_bWKey;
	bool					m_bAKey;
	bool					m_bSKey;
	bool					m_bDKey;
	bool					m_bJump;
	bool					m_bAlt;
	bool					m_bShift;


	void generateTargetBox(ID3D11Device* pd3dDevice);

	/* UI */	
	HUDDialogResourceManager*	m_DialogResourceManager;
	CDXUTTextHelper*            m_pDebugText;
	bool						m_showDebug;
	/* ITEMS*/
	Inventory*					m_Inventory;

	Loader*						m_pLoader;
	bool						m_SafeFiles;

	/* Player */
	Player*						m_pPlayer;
	HUDDialog					m_LifeHUD;
	HUDInventoryDialog			m_InventoryHUD;

	/* Sound */
	SoundEngine*				m_pSound;
protected:
	void drawDebugText();
public:
	RenderableWorld(CFirstPersonCamera * cam, HUDDialogResourceManager* manager);
	RenderableWorld(){};
	~RenderableWorld();

	//Idle
	void FrameMove( double fTime, float fElapsedTime);

	D3DXVECTOR3 getPlayerPosition(int index);

	int generateVB();
	int generateVB(int x, int z);

	int Release();
	int Release(int x, int z);

	Block	breakBlock(int x, int z, int y);

	void setDirty(int x, int z, int y, bool immediatly);
	int renewDirty(bool immediatly);

	//Directx11
	void OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, double fTime, float fElapsedTime, void* pUserContext );

	void OnD3D11CreateDevice( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
	void OnD3D11DestroyDevice( void* pUserContext );

	void OnD3D11ResizedSwapChain( ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
	void OnD3D11ReleasingSwapChain( void* pUserContext );

	// Handling methods
	void handleMouse( bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown, bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta, int xPos, int yPos, void* pUserContext );
	void handleKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
	bool MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
};

class ClientWorld : public RenderableWorld {
private:
	ClientMainThread * m_pConnection;
public:
	 ClientWorld(CFirstPersonCamera * cam, HUDDialogResourceManager* manager);
	~ClientWorld();

	int loadRegion(int x, int z);

	int setCompressedCluster(int x, int z, byte *  memory, uLong   size);

	int		placeBlock(int x, int z, int y, Block block);
	Block	breakBlock(int x, int z, int y);
};
#endif

#ifdef SERVER

struct PlayerData {
	//int				ID;
	Loader*			pLoader;
	D3DXVECTOR3		position;
	INT64			lastUpdate;
};

class ServerWorld : public World {
private:
	ServerMainThread *			m_pConnection;
	std::map<int,PlayerData>	m_Player;
	Loader*						m_pLoader;
public:
	 ServerWorld();
	~ServerWorld();

	void connectPlayer(int ID);
	void disconnectPlayer(int ID);

	void setPlayerPosition(D3DXVECTOR3 pos, int ID);
	D3DXVECTOR3 getPlayerPosition(int ID);

	int setCompressedCluster(int x, int z, byte *  memory, uLong   size);
	int getCompressedCluster(int x, int z, byte ** memory, uLong * size);
};
#endif

#endif